Loading the required packages for the rest of the script. Run once per session.

library(Seurat)
library(sctransform)
library(gridExtra)
library(dplyr)
library(tximport)
library(Matrix)
library(ggplot2)
library(stringr)
library(SeuratWrappers)

0.1 Read big Seurat

Here we read the unfiltered Seurat object with 80,235 cells, processed with SCTransform. And the filtered Seurat object with 52,638 cells, processed with SCTransform. The filtered version we used a cutoff of nFeature_RNA > 800 and percent.mt < 15.

For both, UMAp, Neighbors and Clusters were calculated.

0.2 Sample Big Seurat into Small Seurat (10,000 cells, named pleuro.2)

Running FindAllMarkers function from Seurat in order to look at clusters of bad quality on a small subset of cells that are evenly and randomly sampled from the big dataset to avoid memory issues and to optimize the code locally before running it on HPC. You won’t have to do this on the DEVELOPMENTAL dataset. For screening bad clusters, do FeaturePlot(object, features = c(“percent.mt”,“nFeature_RNA”)) and take those that have high percent.mt and low nFeature_RNA to train your classifier.

# You can run this part as an example of how it works
pleuro.2 <- pleuro_filter[, sample(colnames(pleuro_filter), size = 10000, replace=F)]

#FeaturePlot(new_object, cells = names(new_object@active.ident[new_object@active.ident %in% c("17","30")]), label = T,label.size = 1, pt.size = 0.3, reduction = 'umap', features = c("SATB1","SST")) + NoLegend()

FeaturePlot(pleuro_unfilter, label = T, label.size = 4, pt.size = 0.3, reduction = 'umap', features = c("percent.mt","nFeature_RNA"), ncol = 2) + NoLegend()
FeaturePlot(pleuro_filter, label = T, label.size = 4, pt.size = 0.3, reduction = 'umap', features = c("percent.mt","nFeature_RNA"), ncol = 2) + NoLegend()

You can also run FindAllMarkers function to see whether you can identify clusters that are driven by the expression of mitochondrial genes and/or ribosomal genes and include them as “bad” clusters for the classifier. This function usually takes like 8 hrs locally so I would run it on Ginsburg if you are really invested.

all.markers <- FindAllMarkers(pleuro.2)

FeaturePlot(pleuro.2, label = T, label.size = 4, pt.size = 0.3, reduction = 'umap', features = c("percent.mt","nFeature_RNA"), ncol = 2) + NoLegend()

Screening for good and bad cluster on the basis of marker genes. Some clusters do not have good marker genes to be defined as clusters and/or their differences are being driven by ribosomal/mitocondrial genes.

cluster0 <- subset(all.markers, cluster == 0, select = c(gene, pct.1, pct.2)) # Good
cluster0[,4] <- cluster0$pct.1 - cluster0$pct.2

cluster1 <- subset(all.markers, cluster == 1, select = c(gene, pct.1, pct.2)) # Good cluster
cluster1[,4] <- cluster1$pct.1 - cluster1$pct.2

cluster6 <- subset(all.markers, cluster == 6, select = c(gene, pct.1, pct.2)) # 
cluster6[,4] <- cluster6$pct.1 - cluster6$pct.2

cluster17 <- subset(all.markers, cluster == 17, select = c(gene, pct.1, pct.2)) # BAD RIBO
cluster17[,4] <- cluster17$pct.1 - cluster17$pct.2

cluster21 <- subset(all.markers, cluster == 21, select = c(gene, pct.1, pct.2)) # Good
cluster21[,4] <- cluster21$pct.1 - cluster21$pct.2

cluster24 <- subset(all.markers, cluster == 24, select = c(gene, pct.1, pct.2))
cluster24[,4] <- cluster24$pct.1 - cluster24$pct.2

cluster26 <- subset(all.markers, cluster == 26, select = c(gene, pct.1, pct.2))
cluster26[,4] <- cluster26$pct.1 - cluster26$pct.2

cluster29 <- subset(all.markers, cluster == 29, select = c(gene, pct.1, pct.2))
cluster29[,4] <- cluster29$pct.1 - cluster29$pct.2

cluster40 <- subset(all.markers, cluster == 40, select = c(gene, pct.1, pct.2))
cluster40[,4] <- cluster40$pct.1 - cluster40$pct.2

cluster41 <- subset(all.markers, cluster == 41, select = c(gene, pct.1, pct.2))
cluster41[,4] <- cluster41$pct.1 - cluster41$pct.2

cluster50 <- subset(all.markers, cluster == 50, select = c(gene, pct.1, pct.2))
cluster50[,4] <- cluster50$pct.1 - cluster50$pct.2

cluster52 <- subset(all.markers, cluster == 52, select = c(gene, pct.1, pct.2))
cluster52[,4] <- cluster52$pct.1 - cluster52$pct.2

0.3 Creating meta.data for the classifier

I will use all the bad clusters from this small subset of cells (10,000 cells). For the method to work properly, you have to select all the clusters that look bad, since it will consider as “good” all the rest of the cells. Once you have identified all the bad clusters, make sure to write all their identities in the code. You will want to train your set with ~10% of the total number of “bad cells” that you have (look at length(bad.names)) or table() and sum the bad clusters).

train.final = data.frame(train, ytrain=as.factor(ytrain))
Error in data.frame(train, ytrain = as.factor(ytrain)) : 
  arguments imply differing number of rows: 700, 11000

0.4 Training the classifier

library(e1071)
svmfit=svm(ytrain~., data=train.final, kernel="linear", cost=0.1, scale=F)

set.seed (1)
tune.out=tune(svm,ytrain~.,data=train.final,kernel="linear", ranges=list(cost=c(0.1,1,10,100,500)))
summary(tune.out)


bestmod=tune.out$best.model
summary(bestmod)

test.bad.names<-bad.names[!bad.names %in% train.bad.names] 
test.good.names<-good.names[!good.names %in% train.good.names] 
test.bad<-data.info[test.bad.names,] 
test.good<-data.info[test.good.names,] 
test<-rbind(test.good,test.bad) 

test<-data.info[!rownames(data.info) %in% rownames(train),] 

test<-as.matrix(test)
ytest<-matrix(c(rep(1,length(test.good.names)),rep(-1,length(test.bad.names)))) 

test.final = data.frame(test, ytest=as.factor(ytest))

ypred=predict(bestmod,test.final)
table(predict=ypred, truth=test.final$ytest)

ypred<-as.data.frame(ypred)

0.5 Adding the SVM classification to Seurat Object.

pleuro.2$ypred <- ypred

pred<-as.data.frame(pleuro.2$ypred)
rownames(pred)<-rownames(pleuro.2[[]])
pred[train.bad.names,]<-"-1"
pred[train.good.names,]<-"1"
colnames(pred)<-c("svm_class")
pleuro.2$svm_class <- pred

DimPlot(pleuro.2, split.by="svm_class", pt.size=0.7, label = T, group.by = "library") #+ NoLegend()

0.6 How many cells were thrown away?

# saveRDS(pleuro.2, file = "pleuro2_cleanup.rds")
table(pleuro.2@meta.data$svm_class)

  -1    1 
3110 6890 
pleuro.2 <- RunUMAP(pleuro.2, dims=1:ndims)
13:45:53 UMAP embedding parameters a = 0.9922 b = 1.112
13:45:53 Read 6890 rows and found 120 numeric columns
13:45:53 Using Annoy for neighbor search, n_neighbors = 30
13:45:53 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:45:54 Writing NN index file to temp file /var/folders/h4/17k58rr56z56y6dsspm9qmn40000gp/T//RtmpbeyK9U/file76848e7eb26
13:45:54 Searching Annoy index using 1 thread, search_k = 3000
13:45:56 Annoy recall = 100%
13:45:57 Commencing smooth kNN distance calibration using 1 thread
13:45:58 Initializing from normalized Laplacian + noise
13:45:59 Commencing optimization for 500 epochs, with 270056 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:46:09 Optimization finished
pleuro.2 <- RunUMAP(pleuro.2, dims=1:ndims)
13:46:09 UMAP embedding parameters a = 0.9922 b = 1.112
13:46:09 Read 6890 rows and found 120 numeric columns
13:46:09 Using Annoy for neighbor search, n_neighbors = 30
13:46:09 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:46:10 Writing NN index file to temp file /var/folders/h4/17k58rr56z56y6dsspm9qmn40000gp/T//RtmpbeyK9U/file76871ff312e
13:46:10 Searching Annoy index using 1 thread, search_k = 3000
13:46:12 Annoy recall = 100%
13:46:12 Commencing smooth kNN distance calibration using 1 thread
13:46:14 Initializing from normalized Laplacian + noise
13:46:14 Commencing optimization for 500 epochs, with 270056 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:46:24 Optimization finished
pleuro.2 <- FindNeighbors(pleuro.2, dims=1:ndims)
Computing nearest neighbor graph
Computing SNN
pleuro.2 <- FindClusters(pleuro.2, resolution = 2)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 6890
Number of edges: 234564

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8634
Number of communities: 43
Elapsed time: 0 seconds
DimPlot(pleuro.2, reduction="umap", label=T, pt.size = 0.3) + NoLegend()

I ran AllMarkers as a form of sanity check, just to see whether cluster 0 (which seemed like a blob in the middle) was a good cluster or not. Do not run this locally.

all.markers <-FindAllMarkers(pleuro.2)
cluster0 <- subset(all.markers, cluster == 0, select = c(gene, pct.1, pct.2)) # Bad cluster
cluster0[,4] <- cluster0$pct.1 - cluster0$pct.2

Now it is a good cluster.

0.7 From here, I used the above code but with the large datasets (80,235 and 52,638)

I used the exact same code, only that this time I used the large Seurat objects and ran everything on Ginsburg. I am reading the output file from that code with 36,116 cells for pre-filtered and 41,657 for unfiltered. For this part, I tried different nPCs (ndims) in the code with different resolution.

After looking at the code together and long deliberation, we chose to stick to cluster resolution = 2. Next, we will subset this object to keep only the neurons. The rest of the work to be done with this object is showing neuronal and non-neuronal cells in a general way.

png(paste0("UMAP_byanimal.png"), width=48, height=12, units = "in", res=400)
DimPlot(pleuro, reduction="umap", label=F, pt.size = 0.1, split.by = "animal") + NoLegend()
dev.off()
null device 
          1 
pdf(paste0("UMAP_byanimal.pdf"), width=48, height=12)
DimPlot(pleuro, reduction="umap", label=F, pt.size = 0.1, split.by = "animal") + NoLegend()
dev.off()
null device 
          1 

1 FindAllMarkers for Subset pleuro (37K)

# RUN IN CLUSTER
all.markers <- FindAllMarkers(pleuro)

2 FeaturePLots

A couple more FeaturePlots as we continue to check whether everything is ok.

3 Quality control of the final dataset

3.1 VlnPlot

Violin plots for nFeature, nCount and percent.mt grouped by animal and by library, in the cleaned dataset.

png(paste0("Vln_quality_cluster2.png"), width=10, height=6, units = "in", res=400)
VlnPlot(pleuro, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), pt.size = 0, group.by = "animal", ncol = 3)
dev.off()
null device 
          1 
pdf(paste0("Vln_quality_cluster2.pdf"), width=10, height=6)
VlnPlot(pleuro, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), pt.size = 0, group.by = "animal", ncol = 3)
dev.off()
null device 
          1 
VlnPlot(pleuro, features = c("nFeature_RNA", "nCount_RNA"), group.by = "library", ncol = 2)

saveRDS(pleuro, file = "pleuro_f_final.rds")
#saveRDS(pleuro.uf, file = "pleuro_uf_final.rds")

Getting the information by cluster.

all.markers <- read.csv(file = "all_markers_neurons_res2.csv")
list_markers <- list()
a <- 0
for(i in 1:max(all.markers$cluster)) {
  cluster <- subset(all.markers, cluster == a, select = c(gene, pct.1, pct.2))
  cluster[,4] <- cluster$pct.1 - cluster$pct.2
  list_markers[[i]] <- cluster
  a <- a + 1
}

4 Generating a merged salamander dataset by general cell type

new.cluster.ids <- c('Excitatory telencephalic' #0
,'Excitatory telencephalic'
,'Excitatory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Excitatory telencephalic'
,'Inhibitory telencephalic' #10
,'Immature neurons'
,'Excitatory telencephalic' #12
,'Inhibitory telencephalic'
,'Excitatory telencephalic'
,'Excitatory telencephalic' 
,'Excitatory telencephalic'
,'Excitatory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic' #19
,'Oligodendrocyte precursor cells' 
,'Ependymoglia'
,'Inhibitory telencephalic' #22
,'Ependymoglia'
,'Inhibitory telencephalic' #24
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Non-telencephalic'
,'Inhibitory telencephalic'
,'Excitatory telencephalic'
,'Excitatory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic' #33
,'Microglia' 
,'Excitatory telencephalic'#35
,'Ependymoglia'
,'Excitatory telencephalic' #37
,'Excitatory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Excitatory telencephalic'
,'Inhibitory telencephalic' #43
,'Vascular leptomeningeal cells' 
,'Inhibitory telencephalic' #45
,'Non-telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Non-telencephalic' 
,'Inhibitory telencephalic'
,'Inhibitory telencephalic' #52
,'Immature neurons'
,'Inhibitory telencephalic' #54
,'Inhibitory telencephalic'
,'Excitatory telencephalic'
,'Excitatory telencephalic'
,'Inhibitory telencephalic'
,'Non-telencephalic'
,'Inhibitory telencephalic' #60
,'Olfactory ensheating cells'
,'Inhibitory telencephalic' #62
,'Perivascular macrophages'
,'Inhibitory telencephalic' #64
,'Inhibitory telencephalic'
,'Oligodendrocytes'
,'Non-telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic'
,'Inhibitory telencephalic')
names(new.cluster.ids) <- levels(pleuro)
pleuro.dotplot <- RenameIdents(pleuro, new.cluster.ids)

#saveRDS(pleuro.dotplot, file = "pleuro_dotplot.rds")

5 Loading merged pleuro object and ordering the data

Setting the color palette for salamander UMAP, ordering the clusters and generating UMAP and DotPlot.

5.1 Code for general DotPlot

png(filename = "dotplot_fig1.png", width = 10, height = 6, units = "in", res = 400)
DotPlot(pleuro.dotplot, features = c("SNAP25", "SYT1", "RBFOX3", "FOXG1", "SLC17A7", "GAD1","SLC17A6",  "SOX4", "SOX9", "SOX2", "GFAP", "PDGFRA", "NINJ2", "COL1A2","PRSS56", "C1QB", "LCP1"), idents = c("Excitatory telencephalic", "Inhibitory telencephalic", "Non-telencephalic", "Immature neurons", "Ependymoglia", "Oligodendrocyte precursor cells", "Vascular leptomeningeal cells", "Microglia", "Olfactory ensheating cells", "Oligodendrocytes", "Perivascular macrophages")) + RotatedAxis()
dev.off()
null device 
          1 
pdf(file = "dotplot_fig1.pdf", width = 10, height = 6)
DotPlot(pleuro.dotplot, features = c("SNAP25", "SYT1", "RBFOX3", "FOXG1", "SLC17A7", "GAD1","SLC17A6",  "SOX4", "SOX9", "SOX2", "GFAP", "PDGFRA", "NINJ2", "COL1A2","PRSS56", "C1QB", "LCP1"), idents = c("Excitatory telencephalic", "Inhibitory telencephalic", "Non-telencephalic", "Immature neurons", "Ependymoglia", "Oligodendrocyte precursor cells", "Vascular leptomeningeal cells", "Microglia", "Olfactory ensheating cells", "Oligodendrocytes", "Perivascular macrophages")) + RotatedAxis()
dev.off()
null device 
          1 

6 Subsetting Neurons

We subsetted neurons on the basis of neuronal gene expression (SYT1, SNAP25, SLC17A7, SLC17A6, GAD1, GAD2) and re-ran the pipeline for clustering. We RunPCA with 400 pcs, analyzed the ElbowPlot and determined 180 as a good number to calculate UMAP, Neighbors. In terms of clustering, after long deliberation, we settled to res = 6 as it captured most of the cell diversity. Some adjustments to the neuronal object will be made depending on the details of the clustering.

neurons <- subset(pleuro, idents = c('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '12', '13', '14', '15', '16', '17', '18', '19', '22', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '35', '37', '38', '39', '40', '41', '42', '43', '45', '46', '47', '48', '49', '50', '51', '52', '54', '55', '56', '57', '58', '59', '60', '62', '64', '65', '66', '67', '68', '69', '70'))

#neurons <- RunPCA(neurons, npcs = 400)

ElbowPlot(neurons, ndims = 350)

nPCs = 180
  
neurons <- RunUMAP(neurons, dims = 1:nPCs, verbose = FALSE)

neurons <- FindNeighbors(neurons, dims = 1:nPCs, verbose = FALSE)

neurons <- FindClusters(neurons, resolution = 6, verbose = FALSE)

DimPlot(neurons, label = TRUE, reduction = 'umap', label.size = 6, pt.size = 0.7) + NoLegend()

VlnPlot(neurons, features = c("nFeature_RNA", "nCount_RNA"), split.by = "seurat_clusters")
#saveRDS(neurons, file = "neurons_180pcs.rds")

The rest of the neuronal file will be processed in a new R Markdown file.

7 For Glial analysis

We will take only the glial identities in the dataset for a superficial analysis. We are taking all the non-neuronal cells in the dataset and building a heatmap based on known glial gene markers.

oec.markers <- FindMarkers(pleuro.dotplot, ident.1 = "Olfactory ensheating cells")

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~21m 06s      
  |+                                                 | 2 % ~20m 45s      
  |++                                                | 3 % ~20m 31s      
  |++                                                | 4 % ~20m 08s      
  |+++                                               | 5 % ~19m 49s      
  |+++                                               | 6 % ~19m 52s      
  |++++                                              | 7 % ~19m 31s      
  |++++                                              | 8 % ~19m 08s      
  |+++++                                             | 9 % ~18m 47s      
  |+++++                                             | 10% ~18m 27s      
  |++++++                                            | 11% ~18m 09s      
  |++++++                                            | 12% ~17m 51s      
  |+++++++                                           | 13% ~17m 35s      
  |+++++++                                           | 14% ~17m 20s      
  |++++++++                                          | 15% ~17m 04s      
  |++++++++                                          | 16% ~16m 49s      
  |+++++++++                                         | 17% ~16m 35s      
  |+++++++++                                         | 18% ~16m 21s      
  |++++++++++                                        | 19% ~16m 09s      
  |++++++++++                                        | 20% ~15m 57s      
  |+++++++++++                                       | 21% ~15m 44s      
  |+++++++++++                                       | 22% ~15m 31s      
  |++++++++++++                                      | 23% ~15m 21s      
  |++++++++++++                                      | 24% ~15m 10s      
  |+++++++++++++                                     | 25% ~14m 58s      
  |+++++++++++++                                     | 26% ~14m 46s      
  |++++++++++++++                                    | 27% ~14m 34s      
  |++++++++++++++                                    | 28% ~14m 22s      
  |+++++++++++++++                                   | 29% ~14m 09s      
  |+++++++++++++++                                   | 30% ~13m 57s      
  |++++++++++++++++                                  | 31% ~13m 44s      
  |++++++++++++++++                                  | 32% ~13m 31s      
  |+++++++++++++++++                                 | 33% ~13m 19s      
  |+++++++++++++++++                                 | 34% ~13m 08s      
  |++++++++++++++++++                                | 35% ~12m 56s      
  |++++++++++++++++++                                | 36% ~12m 44s      
  |+++++++++++++++++++                               | 37% ~12m 33s      
  |+++++++++++++++++++                               | 38% ~12m 20s      
  |++++++++++++++++++++                              | 39% ~12m 07s      
  |++++++++++++++++++++                              | 40% ~11m 55s      
  |+++++++++++++++++++++                             | 41% ~11m 43s      
  |+++++++++++++++++++++                             | 42% ~11m 30s      
  |++++++++++++++++++++++                            | 43% ~11m 18s      
  |++++++++++++++++++++++                            | 44% ~11m 05s      
  |+++++++++++++++++++++++                           | 45% ~10m 53s      
  |+++++++++++++++++++++++                           | 46% ~10m 41s      
  |++++++++++++++++++++++++                          | 47% ~10m 29s      
  |++++++++++++++++++++++++                          | 48% ~10m 16s      
  |+++++++++++++++++++++++++                         | 49% ~10m 05s      
  |+++++++++++++++++++++++++                         | 50% ~09m 54s      
  |++++++++++++++++++++++++++                        | 51% ~09m 42s      
  |++++++++++++++++++++++++++                        | 52% ~09m 30s      
  |+++++++++++++++++++++++++++                       | 53% ~09m 18s      
  |+++++++++++++++++++++++++++                       | 54% ~09m 06s      
  |++++++++++++++++++++++++++++                      | 55% ~08m 54s      
  |++++++++++++++++++++++++++++                      | 56% ~08m 42s      
  |+++++++++++++++++++++++++++++                     | 57% ~08m 30s      
  |+++++++++++++++++++++++++++++                     | 58% ~08m 18s      
  |++++++++++++++++++++++++++++++                    | 59% ~08m 06s      
  |++++++++++++++++++++++++++++++                    | 60% ~07m 54s      
  |+++++++++++++++++++++++++++++++                   | 61% ~07m 42s      
  |+++++++++++++++++++++++++++++++                   | 62% ~07m 30s      
  |++++++++++++++++++++++++++++++++                  | 63% ~07m 18s      
  |++++++++++++++++++++++++++++++++                  | 64% ~07m 06s      
  |+++++++++++++++++++++++++++++++++                 | 65% ~06m 54s      
  |+++++++++++++++++++++++++++++++++                 | 66% ~06m 42s      
  |++++++++++++++++++++++++++++++++++                | 67% ~06m 31s      
  |++++++++++++++++++++++++++++++++++                | 68% ~06m 19s      
  |+++++++++++++++++++++++++++++++++++               | 69% ~06m 07s      
  |+++++++++++++++++++++++++++++++++++               | 70% ~05m 55s      
  |++++++++++++++++++++++++++++++++++++              | 71% ~05m 43s      
  |++++++++++++++++++++++++++++++++++++              | 72% ~05m 31s      
  |+++++++++++++++++++++++++++++++++++++             | 73% ~05m 19s      
  |+++++++++++++++++++++++++++++++++++++             | 74% ~05m 07s      
  |++++++++++++++++++++++++++++++++++++++            | 75% ~04m 55s      
  |++++++++++++++++++++++++++++++++++++++            | 76% ~04m 43s      
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~04m 31s      
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~04m 20s      
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~04m 08s      
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~03m 56s      
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~03m 44s      
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~03m 32s      
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~03m 20s      
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~03m 08s      
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~02m 57s      
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~02m 45s      
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~02m 33s      
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~02m 21s      
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~02m 10s      
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~01m 58s      
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~01m 46s      
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~01m 34s      
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~01m 23s      
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~01m 11s      
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~59s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~47s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~35s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~24s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~12s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=19m 37s

8 BarPlot

png(filename = "pleuro_stackedbarplot_animal_Wlegend.png", width = 3, height = 5, units = "in", res = 400)
x
dev.off()
null device 
          1 
pdf(file = "pleuro_stackedbarplot_animal_Wlegend.pdf", width = 3, height = 5)
x
dev.off()
null device 
          1 
pleuro.dotplot <- readRDS(file = "pleuro_dotplot.rds")
data <- table(pleuro.dotplot@meta.data$animal, Idents(pleuro.dotplot))
data_percentage <- apply(data, 2, function(x){x*100/sum(x,na.rm=T)})

library(RColorBrewer)
coul <- brewer.pal(5, "Pastel2") 
ynames <- colnames(data_percentage)
par(oma=c(3,1,1,2) + 0.5)
barplot(data_percentage, 
        col = coul, 
        las =2, 
        font.axis = 2, 
        cex.lab = 0.1, 
        legend.text = rownames(data_percentage),
        args.legend = list(x = "right", inset=c(-0.01,0), xpd = TRUE))

png(filename = "pleuro_barplot_animal_Wlegend.png", width = 30, height = 7, units = "in", res = 400)
par(oma=c(1,1,1,2) + 0.5)
barplot(data_percentage, 
        col = coul, 
        las = 2, 
        font.axis = 2, 
        cex.lab = 0.1, 
        legend.text = rownames(data_percentage),
        args.legend = list(x = "right", inset=c(-0.01,0), xpd = TRUE))
dev.off()
null device 
          1 
pdf(file = "pleuro_barplot_animal_Wlegend.pdf", width = 30, height = 7)
par(oma=c(1,1,1,2) + 0.5)
barplot(data_percentage, 
        col = coul, 
        las =2, 
        font.axis = 2, 
        cex.lab = 0.1, 
        legend.text = rownames(data_percentage),
        args.legend = list(x = "right", inset=c(-0.01,0), xpd = TRUE))
dev.off()
null device 
          1 
LS0tCnRpdGxlOiAiU2luZ2xlLWNlbGwgUk5BLVNlcSBhZHVsdCBkYXRhc2V0IGNsZWFudXAiCmF1dGhvcjogQWxvbnNvIE9ydGVnYS1HdXJyb2xhCmRhdGU6IHwgCiAgICAgIHwgU3RhcnRlZCBvbiAxMS8wOS8yMDIxCiAgICAgIHwgQ29tcGlsZWQ6IGByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJUIgJWQsICVZIilgCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiBubwogIGdpdGh1Yl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQphbHdheXNfYWxsb3dfaHRtbDogeWVzCi0tLQoKTG9hZGluZyB0aGUgcmVxdWlyZWQgcGFja2FnZXMgZm9yIHRoZSByZXN0IG9mIHRoZSBzY3JpcHQuIFJ1biBvbmNlIHBlciBzZXNzaW9uLgoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoc2N0cmFuc2Zvcm0pCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHR4aW1wb3J0KQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCmBgYAoKIyMgUmVhZCBiaWcgU2V1cmF0CkhlcmUgd2UgcmVhZCB0aGUgKnVuZmlsdGVyZWQqIFNldXJhdCBvYmplY3Qgd2l0aCA4MCwyMzUgY2VsbHMsIHByb2Nlc3NlZCB3aXRoIFNDVHJhbnNmb3JtLiAKQW5kIHRoZSAqZmlsdGVyZWQqIFNldXJhdCBvYmplY3Qgd2l0aCA1Miw2MzggY2VsbHMsIHByb2Nlc3NlZCB3aXRoIFNDVHJhbnNmb3JtLiBUaGUgKmZpbHRlcmVkKiB2ZXJzaW9uIHdlIHVzZWQgYSBjdXRvZmYgb2YgKipuRmVhdHVyZV9STkEgPiA4MDAqKiBhbmQgKipwZXJjZW50Lm10IDwgMTUqKi4gCgpGb3IgYm90aCwgVU1BcCwgTmVpZ2hib3JzIGFuZCBDbHVzdGVycyB3ZXJlIGNhbGN1bGF0ZWQuIAoKYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgZmlnLmFsaWduPSdjZW50ZXInfQojIFRoZXNlIHR3byBmaWxlcyBhcmUgYWxyZWFkeSBwcm9jZXNzZWQgd2l0aCBTQ1QsIFBDQSwgVU1BUCwgRmluZE5laWdoYm9ycyBhbmQgRmluZENsdXN0ZXJzLiAKcGxldXJvX3VuZmlsdGVyIDwtIHJlYWRSRFMoZmlsZSA9ICJwbGV1cm9faXNvX1NDVDJfYWxsX3VuZmlsdGVyLnJkcyIpCnBsZXVyb19maWx0ZXIgPC0gcmVhZFJEUyhmaWxlID0gInBsZXVyb19pc29fU0NUMl9hbGxfZmlsdGVyLnJkcyIpCgpwYXIobWZyb3c9YygxLDIpKQpEaW1QbG90KHBsZXVyb191bmZpbHRlciwgcmVkdWN0aW9uID0gJ3VtYXAnLCBsYWJlbCA9IFRSVUUsIHB0LnNpemUgPSAwLjIpICsgTm9MZWdlbmQoKQpEaW1QbG90KHBsZXVyb19maWx0ZXIsIHJlZHVjdGlvbiA9ICd1bWFwJywgbGFiZWwgPSBUUlVFLCBwdC5zaXplID0gMC4yKSArIE5vTGVnZW5kKCkKYGBgCgojIyBTYW1wbGUgQmlnIFNldXJhdCBpbnRvIFNtYWxsIFNldXJhdCAoMTAsMDAwIGNlbGxzLCBuYW1lZCBwbGV1cm8uMikKClJ1bm5pbmcgRmluZEFsbE1hcmtlcnMgZnVuY3Rpb24gZnJvbSBTZXVyYXQgaW4gb3JkZXIgdG8gbG9vayBhdCBjbHVzdGVycyBvZiBiYWQgcXVhbGl0eSBvbiBhIHNtYWxsIHN1YnNldCBvZiBjZWxscyB0aGF0IGFyZSBldmVubHkgYW5kIHJhbmRvbWx5IHNhbXBsZWQgZnJvbSB0aGUgYmlnIGRhdGFzZXQgdG8gYXZvaWQgbWVtb3J5IGlzc3VlcyBhbmQgdG8gb3B0aW1pemUgdGhlIGNvZGUgbG9jYWxseSBiZWZvcmUgcnVubmluZyBpdCBvbiBIUEMuICoqWW91IHdvbid0IGhhdmUgdG8gZG8gdGhpcyBvbiB0aGUgREVWRUxPUE1FTlRBTCBkYXRhc2V0KiouIEZvciBzY3JlZW5pbmcgYmFkIGNsdXN0ZXJzLCBkbyAqRmVhdHVyZVBsb3Qob2JqZWN0LCBmZWF0dXJlcyA9IGMoInBlcmNlbnQubXQiLCJuRmVhdHVyZV9STkEiKSkqIGFuZCB0YWtlIHRob3NlIHRoYXQgaGF2ZSBoaWdoICpwZXJjZW50Lm10KiBhbmQgbG93ICpuRmVhdHVyZV9STkEqIHRvIHRyYWluIHlvdXIgY2xhc3NpZmllci4KCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTUsIGV2YWw9RkFMU0V9CiMgWW91IGNhbiBydW4gdGhpcyBwYXJ0IGFzIGFuIGV4YW1wbGUgb2YgaG93IGl0IHdvcmtzCnBsZXVyby4yIDwtIHBsZXVyb19maWx0ZXJbLCBzYW1wbGUoY29sbmFtZXMocGxldXJvX2ZpbHRlciksIHNpemUgPSAxMDAwMCwgcmVwbGFjZT1GKV0KCiNGZWF0dXJlUGxvdChuZXdfb2JqZWN0LCBjZWxscyA9IG5hbWVzKG5ld19vYmplY3RAYWN0aXZlLmlkZW50W25ld19vYmplY3RAYWN0aXZlLmlkZW50ICVpbiUgYygiMTciLCIzMCIpXSksIGxhYmVsID0gVCxsYWJlbC5zaXplID0gMSwgcHQuc2l6ZSA9IDAuMywgcmVkdWN0aW9uID0gJ3VtYXAnLCBmZWF0dXJlcyA9IGMoIlNBVEIxIiwiU1NUIikpICsgTm9MZWdlbmQoKQoKRmVhdHVyZVBsb3QocGxldXJvX3VuZmlsdGVyLCBsYWJlbCA9IFQsIGxhYmVsLnNpemUgPSA0LCBwdC5zaXplID0gMC4zLCByZWR1Y3Rpb24gPSAndW1hcCcsIGZlYXR1cmVzID0gYygicGVyY2VudC5tdCIsIm5GZWF0dXJlX1JOQSIpLCBuY29sID0gMikgKyBOb0xlZ2VuZCgpCkZlYXR1cmVQbG90KHBsZXVyb19maWx0ZXIsIGxhYmVsID0gVCwgbGFiZWwuc2l6ZSA9IDQsIHB0LnNpemUgPSAwLjMsIHJlZHVjdGlvbiA9ICd1bWFwJywgZmVhdHVyZXMgPSBjKCJwZXJjZW50Lm10IiwibkZlYXR1cmVfUk5BIiksIG5jb2wgPSAyKSArIE5vTGVnZW5kKCkKYGBgCgpZb3UgY2FuIGFsc28gcnVuICpGaW5kQWxsTWFya2VycyogZnVuY3Rpb24gdG8gc2VlIHdoZXRoZXIgeW91IGNhbiBpZGVudGlmeSBjbHVzdGVycyB0aGF0IGFyZSBkcml2ZW4gYnkgdGhlIGV4cHJlc3Npb24gb2YgbWl0b2Nob25kcmlhbCBnZW5lcyBhbmQvb3Igcmlib3NvbWFsIGdlbmVzIGFuZCBpbmNsdWRlIHRoZW0gYXMgImJhZCIgY2x1c3RlcnMgZm9yIHRoZSBjbGFzc2lmaWVyLiBUaGlzIGZ1bmN0aW9uIHVzdWFsbHkgdGFrZXMgbGlrZSA4IGhycyBsb2NhbGx5IHNvIEkgd291bGQgcnVuIGl0IG9uIEdpbnNidXJnIGlmIHlvdSBhcmUgcmVhbGx5IGludmVzdGVkLiAKCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTV9CmFsbC5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHBsZXVyby4yKQoKRmVhdHVyZVBsb3QocGxldXJvLjIsIGxhYmVsID0gVCwgbGFiZWwuc2l6ZSA9IDQsIHB0LnNpemUgPSAwLjMsIHJlZHVjdGlvbiA9ICd1bWFwJywgZmVhdHVyZXMgPSBjKCJwZXJjZW50Lm10IiwibkZlYXR1cmVfUk5BIiksIG5jb2wgPSAyKSArIE5vTGVnZW5kKCkKYGBgCgpTY3JlZW5pbmcgZm9yIGdvb2QgYW5kIGJhZCBjbHVzdGVyIG9uIHRoZSBiYXNpcyBvZiBtYXJrZXIgZ2VuZXMuIFNvbWUgY2x1c3RlcnMgZG8gbm90IGhhdmUgZ29vZCBtYXJrZXIgZ2VuZXMgdG8gYmUgZGVmaW5lZCBhcyBjbHVzdGVycyBhbmQvb3IgdGhlaXIgZGlmZmVyZW5jZXMgYXJlIGJlaW5nIGRyaXZlbiBieSByaWJvc29tYWwvbWl0b2NvbmRyaWFsIGdlbmVzLiAKCmBgYHtyIGV2YWw9RkFMU0V9CmNsdXN0ZXIwIDwtIHN1YnNldChhbGwubWFya2VycywgY2x1c3RlciA9PSAwLCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpICMgR29vZApjbHVzdGVyMFssNF0gPC0gY2x1c3RlcjAkcGN0LjEgLSBjbHVzdGVyMCRwY3QuMgoKY2x1c3RlcjEgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDEsIHNlbGVjdCA9IGMoZ2VuZSwgcGN0LjEsIHBjdC4yKSkgIyBHb29kIGNsdXN0ZXIKY2x1c3RlcjFbLDRdIDwtIGNsdXN0ZXIxJHBjdC4xIC0gY2x1c3RlcjEkcGN0LjIKCmNsdXN0ZXI2IDwtIHN1YnNldChhbGwubWFya2VycywgY2x1c3RlciA9PSA2LCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpICMgCmNsdXN0ZXI2Wyw0XSA8LSBjbHVzdGVyNiRwY3QuMSAtIGNsdXN0ZXI2JHBjdC4yCgpjbHVzdGVyMTcgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDE3LCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpICMgQkFEIFJJQk8KY2x1c3RlcjE3Wyw0XSA8LSBjbHVzdGVyMTckcGN0LjEgLSBjbHVzdGVyMTckcGN0LjIKCmNsdXN0ZXIyMSA8LSBzdWJzZXQoYWxsLm1hcmtlcnMsIGNsdXN0ZXIgPT0gMjEsIHNlbGVjdCA9IGMoZ2VuZSwgcGN0LjEsIHBjdC4yKSkgIyBHb29kCmNsdXN0ZXIyMVssNF0gPC0gY2x1c3RlcjIxJHBjdC4xIC0gY2x1c3RlcjIxJHBjdC4yCgpjbHVzdGVyMjQgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDI0LCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCmNsdXN0ZXIyNFssNF0gPC0gY2x1c3RlcjI0JHBjdC4xIC0gY2x1c3RlcjI0JHBjdC4yCgpjbHVzdGVyMjYgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDI2LCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCmNsdXN0ZXIyNlssNF0gPC0gY2x1c3RlcjI2JHBjdC4xIC0gY2x1c3RlcjI2JHBjdC4yCgpjbHVzdGVyMjkgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDI5LCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCmNsdXN0ZXIyOVssNF0gPC0gY2x1c3RlcjI5JHBjdC4xIC0gY2x1c3RlcjI5JHBjdC4yCgpjbHVzdGVyNDAgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDQwLCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCmNsdXN0ZXI0MFssNF0gPC0gY2x1c3RlcjQwJHBjdC4xIC0gY2x1c3RlcjQwJHBjdC4yCgpjbHVzdGVyNDEgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDQxLCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCmNsdXN0ZXI0MVssNF0gPC0gY2x1c3RlcjQxJHBjdC4xIC0gY2x1c3RlcjQxJHBjdC4yCgpjbHVzdGVyNTAgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDUwLCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCmNsdXN0ZXI1MFssNF0gPC0gY2x1c3RlcjUwJHBjdC4xIC0gY2x1c3RlcjUwJHBjdC4yCgpjbHVzdGVyNTIgPC0gc3Vic2V0KGFsbC5tYXJrZXJzLCBjbHVzdGVyID09IDUyLCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCmNsdXN0ZXI1MlssNF0gPC0gY2x1c3RlcjUyJHBjdC4xIC0gY2x1c3RlcjUyJHBjdC4yCmBgYAoKIyMgQ3JlYXRpbmcgbWV0YS5kYXRhIGZvciB0aGUgY2xhc3NpZmllcgoKSSB3aWxsIHVzZSAqKmFsbCB0aGUgYmFkIGNsdXN0ZXJzKiogZnJvbSB0aGlzIHNtYWxsIHN1YnNldCBvZiBjZWxscyAoMTAsMDAwIGNlbGxzKS4gRm9yIHRoZSBtZXRob2QgdG8gd29yayBwcm9wZXJseSwgeW91IGhhdmUgdG8gc2VsZWN0IGFsbCB0aGUgY2x1c3RlcnMgdGhhdCBsb29rIGJhZCwgc2luY2UgaXQgd2lsbCBjb25zaWRlciBhcyAiZ29vZCIgYWxsIHRoZSByZXN0IG9mIHRoZSBjZWxscy4gT25jZSB5b3UgaGF2ZSBpZGVudGlmaWVkIGFsbCB0aGUgYmFkIGNsdXN0ZXJzLCBtYWtlIHN1cmUgdG8gd3JpdGUgYWxsIHRoZWlyIGlkZW50aXRpZXMgaW4gdGhlIGNvZGUuCllvdSB3aWxsIHdhbnQgdG8gdHJhaW4geW91ciBzZXQgd2l0aCB+MTAlIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgImJhZCBjZWxscyIgdGhhdCB5b3UgaGF2ZSAobG9vayBhdCAqbGVuZ3RoKGJhZC5uYW1lcykpKiBvciAqdGFibGUob2JqZWN0QG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMpKiBhbmQgc3VtIHRoZSBiYWQgY2x1c3RlcnMpLiAKCmBgYHtyfQpyaWJvLmdlbmVzIDwtIHJlYWQudGFibGUoZmlsZSA9ICIvVXNlcnMvYW8yNzIxL0RvY3VtZW50cy9Ub3NjaGVzIExhYiAvUi9TaW5nbGVDZWxsVG9zY2hlcy9yaWJvc29tYWxfZ2VuZXMudHh0IikKcmliby5nZW5lcyA8LSBhcy5jaGFyYWN0ZXIocmliby5nZW5lc1ssMV0pCgpyaWJvLmdlbmVzIDwtIHJpYm8uZ2VuZXNbcmliby5nZW5lcyAlaW4lIHJvd25hbWVzKHBsZXVyby4yKV0KcGxldXJvLjJbWyJwZXJjZW50LnJiIl1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KHBsZXVyby4yLCBmZWF0dXJlcyA9IHJpYm8uZ2VuZXMpCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgY29udGFpbmluZyBtZXRhZGF0YQpkYXRhLmluZm8gPC0gcGxldXJvLjJAbWV0YS5kYXRhWyxjKCJuQ291bnRfUk5BIiwibkZlYXR1cmVfUk5BIiwicGVyY2VudC5tdCIsInBlcmNlbnQucmIiKV0KY2VsbC5uYW1lcyA8LSBjb2xuYW1lcyhwbGV1cm8uMikKCiMgSGVyZSB5b3UgYWRkIGFsbCB0aGUgaWRlbnRzIGZyb20gYmFkIGNsdXN0ZXJzCmJhZC5uYW1lcyA8LSBjb2xuYW1lcyhzdWJzZXQoeCA9IHBsZXVyby4yLCBpZGVudHMgPSBjKDAsIDEsIDQyLCA0OSwgNDQsIDUsIDIxLCA3NSwgMjQsIDY0LCA2LCAzNCwgNjYpKSkKZ29vZC5uYW1lcyA8LSBjZWxsLm5hbWVzWyFjZWxsLm5hbWVzICVpbiUgYmFkLm5hbWVzXSAjIFRoZSBnb29kIGNsdXN0ZXJzIG11c3QgYmUgdGhlIHJlc3Qgb2YgdGhlIGNlbGxzCgp0cmFpbi5iYWQubmFtZXM8LXNhbXBsZShiYWQubmFtZXMsMjEwKSAjIHNlbGVjdHMgcmFuZG9tbHkgWCBiYWQgY2VsbHMgZm9yIHRyYWluaW5nIHNldC4gWW91IHdpbGwgd2FudCB0byB0cmFpbiB5b3VyIHNldCB3aXRoIH4xMCUgb2YgdGhlIHRvdGFsIG51bWJlciBvZiAiYmFkIGNlbGxzIiB0aGF0IHlvdSBoYXZlIChsb29rIGF0IGxlbmd0aChiYWQubmFtZXMpKQp0cmFpbi5nb29kLm5hbWVzPC1zYW1wbGUoZ29vZC5uYW1lcywyMTApICMgc2VsZWN0cyByYW5kb21seSBYIGdvb2QgY2VsbHMgZm9yIHRyYWluaW5nIHNldC4gS2VlcCB0aGUgc2FtZSBudW1iZXIgb2YgY2VsbHMgYXMgYWJvdmUuCgp0cmFpbi5iYWQ8LWRhdGEuaW5mb1t0cmFpbi5iYWQubmFtZXMsXQp0cmFpbi5nb29kPC1kYXRhLmluZm9bdHJhaW4uZ29vZC5uYW1lcyxdCnRyYWluPC1yYmluZCh0cmFpbi5nb29kLHRyYWluLmJhZCkKeXRyYWluIDwtIG1hdHJpeChjKHJlcCgxLDIxMCkscmVwKC0xLDIxMCkpKSAjIExpbWl0cyBhcmUgdGhlIHNhbWUgYXMgeW91ciBudW1iZXIgb2YgdHJhaW5pZyBjZWxscwoKdHJhaW4uZmluYWwgPSBkYXRhLmZyYW1lKHRyYWluLCB5dHJhaW49YXMuZmFjdG9yKHl0cmFpbikpCmBgYAoKIyMgVHJhaW5pbmcgdGhlIGNsYXNzaWZpZXIKCmBgYHtyfQpsaWJyYXJ5KGUxMDcxKQpzdm1maXQ9c3ZtKHl0cmFpbn4uLCBkYXRhPXRyYWluLmZpbmFsLCBrZXJuZWw9ImxpbmVhciIsIGNvc3Q9MC4xLCBzY2FsZT1GKQoKc2V0LnNlZWQgKDEpCnR1bmUub3V0PXR1bmUoc3ZtLHl0cmFpbn4uLGRhdGE9dHJhaW4uZmluYWwsa2VybmVsPSJsaW5lYXIiLCByYW5nZXM9bGlzdChjb3N0PWMoMC4xLDEsMTAsMTAwLDUwMCkpKQpzdW1tYXJ5KHR1bmUub3V0KQoKCmJlc3Rtb2Q9dHVuZS5vdXQkYmVzdC5tb2RlbApzdW1tYXJ5KGJlc3Rtb2QpCgp0ZXN0LmJhZC5uYW1lczwtYmFkLm5hbWVzWyFiYWQubmFtZXMgJWluJSB0cmFpbi5iYWQubmFtZXNdIAp0ZXN0Lmdvb2QubmFtZXM8LWdvb2QubmFtZXNbIWdvb2QubmFtZXMgJWluJSB0cmFpbi5nb29kLm5hbWVzXSAKdGVzdC5iYWQ8LWRhdGEuaW5mb1t0ZXN0LmJhZC5uYW1lcyxdIAp0ZXN0Lmdvb2Q8LWRhdGEuaW5mb1t0ZXN0Lmdvb2QubmFtZXMsXSAKdGVzdDwtcmJpbmQodGVzdC5nb29kLHRlc3QuYmFkKSAKCnRlc3Q8LWRhdGEuaW5mb1shcm93bmFtZXMoZGF0YS5pbmZvKSAlaW4lIHJvd25hbWVzKHRyYWluKSxdIAoKdGVzdDwtYXMubWF0cml4KHRlc3QpCnl0ZXN0PC1tYXRyaXgoYyhyZXAoMSxsZW5ndGgodGVzdC5nb29kLm5hbWVzKSkscmVwKC0xLGxlbmd0aCh0ZXN0LmJhZC5uYW1lcykpKSkgCgp0ZXN0LmZpbmFsID0gZGF0YS5mcmFtZSh0ZXN0LCB5dGVzdD1hcy5mYWN0b3IoeXRlc3QpKQoKeXByZWQ9cHJlZGljdChiZXN0bW9kLHRlc3QuZmluYWwpCnRhYmxlKHByZWRpY3Q9eXByZWQsIHRydXRoPXRlc3QuZmluYWwkeXRlc3QpCgp5cHJlZDwtYXMuZGF0YS5mcmFtZSh5cHJlZCkKYGBgCgojIyBBZGRpbmcgdGhlIFNWTSBjbGFzc2lmaWNhdGlvbiB0byBTZXVyYXQgT2JqZWN0LgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9Nn0KcGxldXJvLjIkeXByZWQgPC0geXByZWQKCnByZWQ8LWFzLmRhdGEuZnJhbWUocGxldXJvLjIkeXByZWQpCnJvd25hbWVzKHByZWQpPC1yb3duYW1lcyhwbGV1cm8uMltbXV0pCnByZWRbdHJhaW4uYmFkLm5hbWVzLF08LSItMSIKcHJlZFt0cmFpbi5nb29kLm5hbWVzLF08LSIxIgpjb2xuYW1lcyhwcmVkKTwtYygic3ZtX2NsYXNzIikKcGxldXJvLjIkc3ZtX2NsYXNzIDwtIHByZWQKCkRpbVBsb3QocGxldXJvLjIsIHNwbGl0LmJ5PSJzdm1fY2xhc3MiLCBwdC5zaXplPTAuNywgbGFiZWwgPSBULCBncm91cC5ieSA9ICJsaWJyYXJ5IikgIysgTm9MZWdlbmQoKQpgYGAKIyMgSG93IG1hbnkgY2VsbHMgd2VyZSB0aHJvd24gYXdheT8gCgpgYGB7cn0KIyBzYXZlUkRTKHBsZXVyby4yLCBmaWxlID0gInBsZXVybzJfY2xlYW51cC5yZHMiKQp0YWJsZShwbGV1cm8uMkBtZXRhLmRhdGEkc3ZtX2NsYXNzKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTN9CnBsZXVyby4zIDwtIHN1YnNldChwbGV1cm8uMiwgY2VsbHM9cm93bmFtZXMocGxldXJvLjJAbWV0YS5kYXRhW3BsZXVyby4yQG1ldGEuZGF0YSRzdm1fY2xhc3M9PSIxIixdKSkKCnBsZXVyby4yIDwtIHBsZXVyby4zCnJtKHBsZXVyby4zKQoKcGxldXJvLjIgPC0gc3Vic2V0KHBsZXVyby4yLCBjZWxscz1yb3duYW1lcyhwbGV1cm8uMkBtZXRhLmRhdGFbcGxldXJvLjJAbWV0YS5kYXRhJG5GZWF0dXJlX1JOQT44MDAsXSkpCgpwbGV1cm8uMiA8LSBTQ1RyYW5zZm9ybShwbGV1cm8uMiwgdmFycy50by5yZWdyZXNzID0gYygicGVyY2VudC5tdCIsIm5GZWF0dXJlX1JOQSIsIm5Db3VudF9STkEiLCJhbmltYWwiKSwgdmVyYm9zZSA9IEZBTFNFKQoKcGxldXJvLjIgPC0gUnVuUENBKHBsZXVyby4yLCBucGNzPTIwMCkKCkVsYm93UGxvdChwbGV1cm8uMiwgbmRpbXM9MjAwKQpuZGltcz0gMTIwCgpwbGV1cm8uMiA8LSBSdW5VTUFQKHBsZXVyby4yLCBkaW1zPTE6bmRpbXMpCnBsZXVyby4yIDwtIEZpbmROZWlnaGJvcnMocGxldXJvLjIsIGRpbXM9MTpuZGltcykKCnBsZXVyby4yIDwtIEZpbmRDbHVzdGVycyhwbGV1cm8uMiwgcmVzb2x1dGlvbiA9IDIpCgpEaW1QbG90KHBsZXVyby4yLCByZWR1Y3Rpb249InVtYXAiLCBsYWJlbD1ULCBwdC5zaXplID0gMC4zKSArIE5vTGVnZW5kKCkKYGBgCgpJIHJhbiBBbGxNYXJrZXJzIGFzIGEgZm9ybSBvZiBzYW5pdHkgY2hlY2ssIGp1c3QgdG8gc2VlIHdoZXRoZXIgY2x1c3RlciAwICh3aGljaCBzZWVtZWQgbGlrZSBhIGJsb2IgaW4gdGhlIG1pZGRsZSkgd2FzIGEgZ29vZCBjbHVzdGVyIG9yIG5vdC4gKipEbyBub3QgcnVuIHRoaXMgbG9jYWxseS4qKiAKCmBgYHtyIGV2YWw9RkFMU0V9CmFsbC5tYXJrZXJzIDwtRmluZEFsbE1hcmtlcnMocGxldXJvLjIpCmNsdXN0ZXIwIDwtIHN1YnNldChhbGwubWFya2VycywgY2x1c3RlciA9PSAwLCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpICMgQmFkIGNsdXN0ZXIKY2x1c3RlcjBbLDRdIDwtIGNsdXN0ZXIwJHBjdC4xIC0gY2x1c3RlcjAkcGN0LjIKYGBgCgpOb3cgaXQgaXMgYSBnb29kIGNsdXN0ZXIuCgojIyBGcm9tIGhlcmUsIEkgdXNlZCB0aGUgYWJvdmUgY29kZSBidXQgd2l0aCB0aGUgbGFyZ2UgZGF0YXNldHMgKDgwLDIzNSBhbmQgNTIsNjM4KQoKSSB1c2VkIHRoZSBleGFjdCBzYW1lIGNvZGUsIG9ubHkgdGhhdCB0aGlzIHRpbWUgSSB1c2VkIHRoZSBsYXJnZSBTZXVyYXQgb2JqZWN0cyBhbmQgcmFuIGV2ZXJ5dGhpbmcgb24gR2luc2J1cmcuIEkgYW0gcmVhZGluZyB0aGUgb3V0cHV0IGZpbGUgZnJvbSB0aGF0IGNvZGUgd2l0aCAqMzYsMTE2IGNlbGxzIGZvciBwcmUtZmlsdGVyZWQqIGFuZCAqNDEsNjU3IGZvciB1bmZpbHRlcmVkKi4gRm9yIHRoaXMgcGFydCwgSSB0cmllZCBkaWZmZXJlbnQgblBDcyAobmRpbXMpIGluIHRoZSBjb2RlIHdpdGggZGlmZmVyZW50IHJlc29sdXRpb24uIAoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KcGxldXJvIDwtIHJlYWRSRFMoInBsZXVyb19jbGVhbl9pc29fU0NUX2FsbF9maWx0ZXIucmRzIikKI3BsZXVyby51ZiA8LSByZWFkUkRTKCJwbGV1cm9fY2xlYW5faXNvX1NDVF9hbGxfdW5maWx0ZXIucmRzIikKCnBsZXVybyA8LSBSdW5QQ0EocGxldXJvLCBucGNzPTIwMCkKI3BsZXVyby51ZiA8LSBSdW5QQ0EocGxldXJvLnVmLCBucGNzPTIwMCkKCkVsYm93UGxvdChwbGV1cm8sIG5kaW1zPTIwMCkKI0VsYm93UGxvdChwbGV1cm8udWYsIG5kaW1zPTIwMCkKCm5kaW1zID0gNTAKCnBsZXVybyA8LSBSdW5VTUFQKHBsZXVybywgZGltcz0xOm5kaW1zKQojcGxldXJvLnVmIDwtIFJ1blVNQVAocGxldXJvLnVmLCBkaW1zPTE6bmRpbXMpCgpwbGV1cm8gPC0gRmluZE5laWdoYm9ycyhwbGV1cm8sIGRpbXM9MTpuZGltcykKI3BsZXVyby51ZiA8LSBGaW5kTmVpZ2hib3JzKHBsZXVyby51ZiwgZGltcz0xOm5kaW1zKQoKcGxldXJvIDwtIEZpbmRDbHVzdGVycyhwbGV1cm8sIHJlc29sdXRpb24gPSAyKQojcGxldXJvLnVmIDwtIEZpbmRDbHVzdGVycyhwbGV1cm8udWYsIHJlc29sdXRpb24gPSAyKQoKRGltUGxvdChwbGV1cm8sIHJlZHVjdGlvbj0idW1hcCIsIGxhYmVsPVQsIHB0LnNpemUgPSAwLjEpICsgTm9MZWdlbmQoKQojRGltUGxvdChwbGV1cm8udWYsIHJlZHVjdGlvbj0idW1hcCIsIGxhYmVsPVQsIHB0LnNpemUgPSAwLjEpICsgTm9MZWdlbmQoKSArIGdndGl0bGUoJ1VuZmlsdGVyZWQnKQpgYGAKCkFmdGVyIGxvb2tpbmcgYXQgdGhlIGNvZGUgdG9nZXRoZXIgYW5kIGxvbmcgZGVsaWJlcmF0aW9uLCB3ZSBjaG9zZSB0byBzdGljayB0byAqKmNsdXN0ZXIgcmVzb2x1dGlvbiA9IDIqKi4gTmV4dCwgd2Ugd2lsbCBzdWJzZXQgdGhpcyBvYmplY3QgdG8ga2VlcCBvbmx5IHRoZSBuZXVyb25zLiBUaGUgcmVzdCBvZiB0aGUgd29yayB0byBiZSBkb25lIHdpdGggdGhpcyBvYmplY3QgaXMgc2hvd2luZyBuZXVyb25hbCBhbmQgbm9uLW5ldXJvbmFsIGNlbGxzIGluIGEgZ2VuZXJhbCB3YXkuIAoKYGBge3J9CnBuZyhwYXN0ZTAoIlVNQVBfYnlhbmltYWwucG5nIiksIHdpZHRoPTQ4LCBoZWlnaHQ9MTIsIHVuaXRzID0gImluIiwgcmVzPTQwMCkKRGltUGxvdChwbGV1cm8sIHJlZHVjdGlvbj0idW1hcCIsIGxhYmVsPUYsIHB0LnNpemUgPSAwLjEsIHNwbGl0LmJ5ID0gImFuaW1hbCIpICsgTm9MZWdlbmQoKQpkZXYub2ZmKCkKYGBgCgpgYGB7cn0KcGRmKHBhc3RlMCgiVU1BUF9ieWFuaW1hbC5wZGYiKSwgd2lkdGg9NDgsIGhlaWdodD0xMikKRGltUGxvdChwbGV1cm8sIHJlZHVjdGlvbj0idW1hcCIsIGxhYmVsPUYsIHB0LnNpemUgPSAwLjEsIHNwbGl0LmJ5ID0gImFuaW1hbCIpICsgTm9MZWdlbmQoKQpkZXYub2ZmKCkKYGBgCgojIEZpbmRBbGxNYXJrZXJzIGZvciBTdWJzZXQgcGxldXJvICgzN0spCgpgYGB7ciBldmFsPUZBTFNFfQojIFJVTiBJTiBDTFVTVEVSCmFsbC5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHBsZXVybykKYGBgCgojIEZlYXR1cmVQTG90cwoKQSBjb3VwbGUgbW9yZSBGZWF0dXJlUGxvdHMgYXMgd2UgY29udGludWUgdG8gY2hlY2sgd2hldGhlciBldmVyeXRoaW5nIGlzIG9rLiAKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CkZlYXR1cmVQbG90KHBsZXVybywgYygiU05BUDI1IiwiTkVVUk9ENiIsIkRMWDUiLCJHTFVMIiksIHB0LnNpemU9MC4zKQpGZWF0dXJlUGxvdChwbGV1cm8sIGMoIk5QWSIsIkNCTE40IiwiU1A5IiwiU09YNiIpLCBwdC5zaXplPTAuMykKRmVhdHVyZVBsb3QocGxldXJvLCBjKCJaTkY1MzYiLCJMSFg2IiwiQURBUkIyIiwiSFBDQSIpLCBwdC5zaXplPTAuMykKYGBgCgojIFF1YWxpdHkgY29udHJvbCBvZiB0aGUgZmluYWwgZGF0YXNldAoKIyMgVmxuUGxvdAoKVmlvbGluIHBsb3RzIGZvciBuRmVhdHVyZSwgbkNvdW50IGFuZCBwZXJjZW50Lm10IGdyb3VwZWQgYnkgYW5pbWFsIGFuZCBieSBsaWJyYXJ5LCBpbiB0aGUgY2xlYW5lZCBkYXRhc2V0LiAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTZ9CnBuZyhwYXN0ZTAoIlZsbl9xdWFsaXR5X2NsdXN0ZXIyLnBuZyIpLCB3aWR0aD0xMCwgaGVpZ2h0PTYsIHVuaXRzID0gImluIiwgcmVzPTQwMCkKVmxuUGxvdChwbGV1cm8sIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBwdC5zaXplID0gMCwgZ3JvdXAuYnkgPSAiYW5pbWFsIiwgbmNvbCA9IDMpCmRldi5vZmYoKQpgYGAKYGBge3J9CnBkZihwYXN0ZTAoIlZsbl9xdWFsaXR5X2NsdXN0ZXIyLnBkZiIpLCB3aWR0aD0xMCwgaGVpZ2h0PTYpClZsblBsb3QocGxldXJvLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgcHQuc2l6ZSA9IDAsIGdyb3VwLmJ5ID0gImFuaW1hbCIsIG5jb2wgPSAzKQpkZXYub2ZmKCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMn0KVmxuUGxvdChwbGV1cm8sIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiKSwgZ3JvdXAuYnkgPSAibGlicmFyeSIsIG5jb2wgPSAyKQpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CnNhdmVSRFMocGxldXJvLCBmaWxlID0gInBsZXVyb19mX2ZpbmFsLnJkcyIpCiNzYXZlUkRTKHBsZXVyby51ZiwgZmlsZSA9ICJwbGV1cm9fdWZfZmluYWwucmRzIikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwbGV1cm8gPC0gUnVuUENBKHBsZXVybywgbnBjcz0yMDApCgpFbGJvd1Bsb3QocGxldXJvLCBuZGltcz0yMDApCgpuZGltcyA9IDUwCgpwbGV1cm8gPC0gUnVuVU1BUChwbGV1cm8sIGRpbXM9MTpuZGltcykKCnBsZXVybyA8LSBGaW5kTmVpZ2hib3JzKHBsZXVybywgZGltcz0xOm5kaW1zKQoKcGxldXJvIDwtIHJlYWRSRFMoInBsZXVyb19mX2ZpbmFsLnJkcyIpCnBsZXVybyA8LSBGaW5kQ2x1c3RlcnMocGxldXJvLCByZXNvbHV0aW9uID0gMC4zKQoKI2NsdTkgPC0gV2hpY2hDZWxscyhwbGV1cm8sIGlkZW50cyA9ICczOScpCkRpbVBsb3QocGxldXJvLCByZWR1Y3Rpb249InVtYXAiLCBsYWJlbD1ULCBwdC5zaXplID0gMC4xLCBsYWJlbC5zaXplID0gMykgKyBOb0xlZ2VuZCgpCgojIEZpbmRBbGxNYXJrZXJzIHdhcyBydW4gb24gR2luc2J1cmcsIGNzdiB3YXMgc2F2ZWQgYW5kIGxvYWRlZCBpbiBoZXJlIAphbGwubWFya2VycyA8LSByZWFkLmNzdihmaWxlID0gImFsbF9tYXJrZXJzX3BsZXVyb19mX2ZpbmFsLmNzdiIsIGhlYWRlciA9IFRSVUUpCmBgYAoKR2V0dGluZyB0aGUgaW5mb3JtYXRpb24gYnkgY2x1c3Rlci4gCgpgYGB7cn0KYWxsLm1hcmtlcnMgPC0gcmVhZC5jc3YoZmlsZSA9ICJhbGxfbWFya2Vyc19uZXVyb25zX3JlczIuY3N2IikKbGlzdF9tYXJrZXJzIDwtIGxpc3QoKQphIDwtIDAKZm9yKGkgaW4gMTptYXgoYWxsLm1hcmtlcnMkY2x1c3RlcikpIHsKICBjbHVzdGVyIDwtIHN1YnNldChhbGwubWFya2VycywgY2x1c3RlciA9PSBhLCBzZWxlY3QgPSBjKGdlbmUsIHBjdC4xLCBwY3QuMikpCiAgY2x1c3RlclssNF0gPC0gY2x1c3RlciRwY3QuMSAtIGNsdXN0ZXIkcGN0LjIKICBsaXN0X21hcmtlcnNbW2ldXSA8LSBjbHVzdGVyCiAgYSA8LSBhICsgMQp9CmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CkZlYXR1cmVQbG90KHBsZXVybywgZmVhdHVyZXMgPSBjKCJTTEMxN0E3IiwiR0FEMSIsIlNMQzdBNiIsIkdBRDIiKSwgcHQuc2l6ZSA9IDAuNSwgbGFiZWwgPSBULCBsYWJlbC5zaXplID0gNCkKYGBgCgojIEdlbmVyYXRpbmcgYSBtZXJnZWQgc2FsYW1hbmRlciBkYXRhc2V0IGJ5IGdlbmVyYWwgY2VsbCB0eXBlCgpgYGB7cn0KbmV3LmNsdXN0ZXIuaWRzIDwtIGMoJ0V4Y2l0YXRvcnkgdGVsZW5jZXBoYWxpYycgIzAKLCdFeGNpdGF0b3J5IHRlbGVuY2VwaGFsaWMnCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0V4Y2l0YXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnICMxMAosJ0ltbWF0dXJlIG5ldXJvbnMnCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJyAjMTIKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0V4Y2l0YXRvcnkgdGVsZW5jZXBoYWxpYycgCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0V4Y2l0YXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJyAjMTkKLCdPbGlnb2RlbmRyb2N5dGUgcHJlY3Vyc29yIGNlbGxzJyAKLCdFcGVuZHltb2dsaWEnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJyAjMjIKLCdFcGVuZHltb2dsaWEnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJyAjMjQKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdOb24tdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0V4Y2l0YXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJyAjMzMKLCdNaWNyb2dsaWEnIAosJ0V4Y2l0YXRvcnkgdGVsZW5jZXBoYWxpYycjMzUKLCdFcGVuZHltb2dsaWEnCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJyAjMzcKLCdFeGNpdGF0b3J5IHRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycgIzQzCiwnVmFzY3VsYXIgbGVwdG9tZW5pbmdlYWwgY2VsbHMnIAosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycgIzQ1CiwnTm9uLXRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnTm9uLXRlbGVuY2VwaGFsaWMnIAosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnICM1MgosJ0ltbWF0dXJlIG5ldXJvbnMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJyAjNTQKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljJwosJ0V4Y2l0YXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnTm9uLXRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJyAjNjAKLCdPbGZhY3RvcnkgZW5zaGVhdGluZyBjZWxscycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnICM2MgosJ1Blcml2YXNjdWxhciBtYWNyb3BoYWdlcycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnICM2NAosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdPbGlnb2RlbmRyb2N5dGVzJwosJ05vbi10ZWxlbmNlcGhhbGljJwosJ0luaGliaXRvcnkgdGVsZW5jZXBoYWxpYycKLCdJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMnCiwnSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljJykKbmFtZXMobmV3LmNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMocGxldXJvKQpwbGV1cm8uZG90cGxvdCA8LSBSZW5hbWVJZGVudHMocGxldXJvLCBuZXcuY2x1c3Rlci5pZHMpCgojc2F2ZVJEUyhwbGV1cm8uZG90cGxvdCwgZmlsZSA9ICJwbGV1cm9fZG90cGxvdC5yZHMiKQpgYGAKCiMgTG9hZGluZyBtZXJnZWQgcGxldXJvIG9iamVjdCBhbmQgb3JkZXJpbmcgdGhlIGRhdGEKClNldHRpbmcgdGhlIGNvbG9yIHBhbGV0dGUgZm9yIHNhbGFtYW5kZXIgVU1BUCwgb3JkZXJpbmcgdGhlIGNsdXN0ZXJzIGFuZCBnZW5lcmF0aW5nIFVNQVAgYW5kIERvdFBsb3QuCgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD01fQpwbGV1cm8uZG90cGxvdCA8LSByZWFkUkRTKCJwbGV1cm9fZG90cGxvdC5yZHMiKQpsZXZlbHMocGxldXJvLmRvdHBsb3QpIDwtIGMoIlBlcml2YXNjdWxhciBtYWNyb3BoYWdlcyIsICJNaWNyb2dsaWEiLCAiT2xmYWN0b3J5IGVuc2hlYXRpbmcgY2VsbHMiLCAiVmFzY3VsYXIgbGVwdG9tZW5pbmdlYWwgY2VsbHMiLCAiT2xpZ29kZW5kcm9jeXRlcyIsICAiT2xpZ29kZW5kcm9jeXRlIHByZWN1cnNvciBjZWxscyIsIkVwZW5keW1vZ2xpYSIsICJJbW1hdHVyZSBuZXVyb25zIiwgIk5vbi10ZWxlbmNlcGhhbGljIiwgIkluaGliaXRvcnkgdGVsZW5jZXBoYWxpYyIsICJFeGNpdGF0b3J5IHRlbGVuY2VwaGFsaWMiKQoKb3JkZXIubGlzdCA8LSBsaXN0KCJQZXJpdmFzY3VsYXIgbWFjcm9waGFnZXMiLCAiTWljcm9nbGlhIiwgIk9sZmFjdG9yeSBlbnNoZWF0aW5nIGNlbGxzIiwgIlZhc2N1bGFyIGxlcHRvbWVuaW5nZWFsIGNlbGxzIiwgIk9saWdvZGVuZHJvY3l0ZXMiLCAgIk9saWdvZGVuZHJvY3l0ZSBwcmVjdXJzb3IgY2VsbHMiLCJFcGVuZHltb2dsaWEiLCAiSW1tYXR1cmUgbmV1cm9ucyIsICJOb24tdGVsZW5jZXBoYWxpYyIsICJJbmhpYml0b3J5IHRlbGVuY2VwaGFsaWMiLCAiRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljIikKCnBhbCA8LSBjKCIjMDBBNUZGIiwgIiNGODc2NkQiLCAiIzAwQjgxRiIsICIjMDBDMEI4IiwgIiNFNzZCRjMiLCAiIzk1OTBGRiIsICIjN0NBRTAwIiwgIiNFNzg2MUIiLCAiIzAwQkU2QyIsICIjQkI5RDAwIiwgIiNDNzdDRkYiKQoKcG5nKHBhc3RlMCgiZ2VuZXJhbFVNQVAucG5nIiksIHdpZHRoPTEwLCBoZWlnaHQ9MTAsIHVuaXRzID0gImluIiwgcmVzPTQwMCkKRGltUGxvdChwbGV1cm8uZG90cGxvdCwgcHQuc2l6ZSA9IDAuMDUsIGxhYmVsID0gRiwgcmVkdWN0aW9uID0gJ3VtYXAnLCBjb2xzID0gcGFsLCBvcmRlciA9IG9yZGVyLmxpc3QpICsgTm9MZWdlbmQoKQpkZXYub2ZmKCkKYGBgCgojIyBDb2RlIGZvciBnZW5lcmFsIERvdFBsb3QKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTV9CkRvdFBsb3QocGxldXJvLmRvdHBsb3QsIGZlYXR1cmVzID0gYygiU05BUDI1IiwgIlNZVDEiLCAiUkJGT1gzIiwgIkZPWEcxIiwgIlNMQzE3QTciLCAiR0FEMSIsIlNMQzE3QTYiLCAgIlNPWDQiLCAiU09YOSIsICJTT1gyIiwgIkdGQVAiLCAiUERHRlJBIiwgIk5JTkoyIiwgIkNPTDFBMiIsIlBSU1M1NiIsICJDMVFCIiwgIkxDUDEiKSwgaWRlbnRzID0gYygiRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljIiwgIkluaGliaXRvcnkgdGVsZW5jZXBoYWxpYyIsICJOb24tdGVsZW5jZXBoYWxpYyIsICJJbW1hdHVyZSBuZXVyb25zIiwgIkVwZW5keW1vZ2xpYSIsICJPbGlnb2RlbmRyb2N5dGUgcHJlY3Vyc29yIGNlbGxzIiwgIlZhc2N1bGFyIGxlcHRvbWVuaW5nZWFsIGNlbGxzIiwgIk1pY3JvZ2xpYSIsICJPbGZhY3RvcnkgZW5zaGVhdGluZyBjZWxscyIsICJPbGlnb2RlbmRyb2N5dGVzIiwgIlBlcml2YXNjdWxhciBtYWNyb3BoYWdlcyIpKSArIFJvdGF0ZWRBeGlzKCkKYGBgCgpgYGB7cn0KcG5nKGZpbGVuYW1lID0gImRvdHBsb3RfZmlnMS5wbmciLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsIHJlcyA9IDQwMCkKRG90UGxvdChwbGV1cm8uZG90cGxvdCwgZmVhdHVyZXMgPSBjKCJTTkFQMjUiLCAiU1lUMSIsICJSQkZPWDMiLCAiRk9YRzEiLCAiU0xDMTdBNyIsICJHQUQxIiwiU0xDMTdBNiIsICAiU09YNCIsICJTT1g5IiwgIlNPWDIiLCAiR0ZBUCIsICJQREdGUkEiLCAiTklOSjIiLCAiQ09MMUEyIiwiUFJTUzU2IiwgIkMxUUIiLCAiTENQMSIpLCBpZGVudHMgPSBjKCJFeGNpdGF0b3J5IHRlbGVuY2VwaGFsaWMiLCAiSW5oaWJpdG9yeSB0ZWxlbmNlcGhhbGljIiwgIk5vbi10ZWxlbmNlcGhhbGljIiwgIkltbWF0dXJlIG5ldXJvbnMiLCAiRXBlbmR5bW9nbGlhIiwgIk9saWdvZGVuZHJvY3l0ZSBwcmVjdXJzb3IgY2VsbHMiLCAiVmFzY3VsYXIgbGVwdG9tZW5pbmdlYWwgY2VsbHMiLCAiTWljcm9nbGlhIiwgIk9sZmFjdG9yeSBlbnNoZWF0aW5nIGNlbGxzIiwgIk9saWdvZGVuZHJvY3l0ZXMiLCAiUGVyaXZhc2N1bGFyIG1hY3JvcGhhZ2VzIikpICsgUm90YXRlZEF4aXMoKQpkZXYub2ZmKCkKYGBgCgpgYGB7cn0KcGRmKGZpbGUgPSAiZG90cGxvdF9maWcxLnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDYpCkRvdFBsb3QocGxldXJvLmRvdHBsb3QsIGZlYXR1cmVzID0gYygiU05BUDI1IiwgIlNZVDEiLCAiUkJGT1gzIiwgIkZPWEcxIiwgIlNMQzE3QTciLCAiR0FEMSIsIlNMQzE3QTYiLCAgIlNPWDQiLCAiU09YOSIsICJTT1gyIiwgIkdGQVAiLCAiUERHRlJBIiwgIk5JTkoyIiwgIkNPTDFBMiIsIlBSU1M1NiIsICJDMVFCIiwgIkxDUDEiKSwgaWRlbnRzID0gYygiRXhjaXRhdG9yeSB0ZWxlbmNlcGhhbGljIiwgIkluaGliaXRvcnkgdGVsZW5jZXBoYWxpYyIsICJOb24tdGVsZW5jZXBoYWxpYyIsICJJbW1hdHVyZSBuZXVyb25zIiwgIkVwZW5keW1vZ2xpYSIsICJPbGlnb2RlbmRyb2N5dGUgcHJlY3Vyc29yIGNlbGxzIiwgIlZhc2N1bGFyIGxlcHRvbWVuaW5nZWFsIGNlbGxzIiwgIk1pY3JvZ2xpYSIsICJPbGZhY3RvcnkgZW5zaGVhdGluZyBjZWxscyIsICJPbGlnb2RlbmRyb2N5dGVzIiwgIlBlcml2YXNjdWxhciBtYWNyb3BoYWdlcyIpKSArIFJvdGF0ZWRBeGlzKCkKZGV2Lm9mZigpCmBgYAoKIyBTdWJzZXR0aW5nIE5ldXJvbnMKCldlIHN1YnNldHRlZCBuZXVyb25zIG9uIHRoZSBiYXNpcyBvZiBuZXVyb25hbCBnZW5lIGV4cHJlc3Npb24gKFNZVDEsIFNOQVAyNSwgU0xDMTdBNywgU0xDMTdBNiwgR0FEMSwgR0FEMikgYW5kIHJlLXJhbiB0aGUgcGlwZWxpbmUgZm9yIGNsdXN0ZXJpbmcuIFdlIFJ1blBDQSB3aXRoIDQwMCBwY3MsIGFuYWx5emVkIHRoZSBFbGJvd1Bsb3QgYW5kIGRldGVybWluZWQgMTgwIGFzIGEgZ29vZCBudW1iZXIgdG8gY2FsY3VsYXRlIFVNQVAsIE5laWdoYm9ycy4gSW4gdGVybXMgb2YgY2x1c3RlcmluZywgYWZ0ZXIgbG9uZyBkZWxpYmVyYXRpb24sIHdlIHNldHRsZWQgdG8gKipyZXMgPSA2KiogYXMgaXQgY2FwdHVyZWQgbW9zdCBvZiB0aGUgY2VsbCBkaXZlcnNpdHkuIFNvbWUgYWRqdXN0bWVudHMgdG8gdGhlIG5ldXJvbmFsIG9iamVjdCB3aWxsIGJlIG1hZGUgZGVwZW5kaW5nIG9uIHRoZSBkZXRhaWxzIG9mIHRoZSBjbHVzdGVyaW5nLiAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KbmV1cm9ucyA8LSBzdWJzZXQocGxldXJvLCBpZGVudHMgPSBjKCcwJywgJzEnLCAnMicsICczJywgJzQnLCAnNScsICc2JywgJzcnLCAnOCcsICc5JywgJzEwJywgJzEyJywgJzEzJywgJzE0JywgJzE1JywgJzE2JywgJzE3JywgJzE4JywgJzE5JywgJzIyJywgJzI0JywgJzI1JywgJzI2JywgJzI3JywgJzI4JywgJzI5JywgJzMwJywgJzMxJywgJzMyJywgJzMzJywgJzM1JywgJzM3JywgJzM4JywgJzM5JywgJzQwJywgJzQxJywgJzQyJywgJzQzJywgJzQ1JywgJzQ2JywgJzQ3JywgJzQ4JywgJzQ5JywgJzUwJywgJzUxJywgJzUyJywgJzU0JywgJzU1JywgJzU2JywgJzU3JywgJzU4JywgJzU5JywgJzYwJywgJzYyJywgJzY0JywgJzY1JywgJzY2JywgJzY3JywgJzY4JywgJzY5JywgJzcwJykpCgojbmV1cm9ucyA8LSBSdW5QQ0EobmV1cm9ucywgbnBjcyA9IDQwMCkKCkVsYm93UGxvdChuZXVyb25zLCBuZGltcyA9IDM1MCkKCm5QQ3MgPSAxODAKICAKbmV1cm9ucyA8LSBSdW5VTUFQKG5ldXJvbnMsIGRpbXMgPSAxOm5QQ3MsIHZlcmJvc2UgPSBGQUxTRSkKCm5ldXJvbnMgPC0gRmluZE5laWdoYm9ycyhuZXVyb25zLCBkaW1zID0gMTpuUENzLCB2ZXJib3NlID0gRkFMU0UpCgpuZXVyb25zIDwtIEZpbmRDbHVzdGVycyhuZXVyb25zLCByZXNvbHV0aW9uID0gNiwgdmVyYm9zZSA9IEZBTFNFKQoKRGltUGxvdChuZXVyb25zLCBsYWJlbCA9IFRSVUUsIHJlZHVjdGlvbiA9ICd1bWFwJywgbGFiZWwuc2l6ZSA9IDYsIHB0LnNpemUgPSAwLjcpICsgTm9MZWdlbmQoKQoKVmxuUGxvdChuZXVyb25zLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiksIHNwbGl0LmJ5ID0gInNldXJhdF9jbHVzdGVycyIpCiNzYXZlUkRTKG5ldXJvbnMsIGZpbGUgPSAibmV1cm9uc18xODBwY3MucmRzIikKYGBgCgpUaGUgcmVzdCBvZiB0aGUgbmV1cm9uYWwgZmlsZSB3aWxsIGJlIHByb2Nlc3NlZCBpbiBhIG5ldyBSIE1hcmtkb3duIGZpbGUuIAoKIyBGb3IgR2xpYWwgYW5hbHlzaXMKCldlIHdpbGwgdGFrZSBvbmx5IHRoZSBnbGlhbCBpZGVudGl0aWVzIGluIHRoZSBkYXRhc2V0IGZvciBhIHN1cGVyZmljaWFsIGFuYWx5c2lzLiBXZSBhcmUgdGFraW5nIGFsbCB0aGUgbm9uLW5ldXJvbmFsIGNlbGxzIGluIHRoZSBkYXRhc2V0IGFuZCBidWlsZGluZyBhIGhlYXRtYXAgYmFzZWQgb24ga25vd24gZ2xpYWwgZ2VuZSBtYXJrZXJzLiAKCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpnbGlhIDwtIHN1YnNldChwbGV1cm8uZG90cGxvdCwgaWRlbnRzID0gYygiTWljcm9nbGlhIiwgIk9sZmFjdG9yeSBlbnNoZWF0aW5nIGNlbGxzIiwgIk9saWdvZGVuZHJvY3l0ZXMiLCAgIk9saWdvZGVuZHJvY3l0ZSBwcmVjdXJzb3IgY2VsbHMiLCJFcGVuZHltb2dsaWEiKSkKCmdsaWFsLmdlbmVzIDwtIGMoIlBER0ZSQSIsICJJVFBSMiIsICJCQU1CSSIsICJPTElHMSIsICJPTElHMiIsICJTT1gxMCIsICJOS1gyLTIiLCAiT01HIiwgIkNOUCIsICJDRDkiLCAiTktYNi0yIiwgIk1CUCIsICJQTFAxIiwgIkVSTU4iLCAiQ0xETjExIiwgIkZHRlIyIiwgIklEMSIsICJJRDMiLCAiU09YOSIsICJTT1gyIiwgIlNPWDIxIiwgIkZHRlIzIiwgIlNBTEwzIiwgIlBBWDYiLCAiRU1YMiIsICJWSU0iLCAiU0xDMUEzIiwgIkdGQVAiLCAiQVFQNCIsICJTMTAwQiIsICJHTFVMIiwgIkdKQTEiLCAiRk9YSjEiLCAiWk1ZTkQxMCIsICJSRlgzIiwgIkVGSEMxIiwgIlNQQUc2IiwgIlNQQUcxNyIsICJTUEExNyIsICJXRFIzNSIsICJDQ0RDMTM1IiwgIlRFS1QxIiwgIlRFS1Q0IiwgIlRUTEw5IiwgIlJJQkMyIiwgIlJTUEgxIiwgIkROQUFGMSIsICJEQVcxIiwgIkNDREMzOSIsICJORUs1IiwgIlNQQVRBMTciLCAiSVFDRyIsICJJUkY4IiwgIkhDTFMxIiwgIlNQSTEiLCAiQzFRQyIsICJBUE9FIiwgIlJHUzEiLCAiUkdTMTgiLCAiQUxPWDUiLCAiQ0Q4MyIpCgpnbGlhbC5nZW5lcy4yIDwtIGdsaWFsLmdlbmVzW2dsaWFsLmdlbmVzICVpbiUgcm93bmFtZXMoZ2xpYUBhc3NheXMkUk5BKV0KCmhlYXRkYXRhIDwtIGFzLm1hdHJpeChnbGlhQGFzc2F5cyRSTkFAZGF0YVtyb3duYW1lcyhnbGlhQGFzc2F5cyRSTkFAZGF0YSkgJWluJSBnbGlhbC5nZW5lcy4yXSkKaGVhdGRhdGFfc2NhbGUgPSB0KHNjYWxlKHQoaGVhdGRhdGEpKSkKCm9lYy5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHBsZXVyby5kb3RwbG90LCBpZGVudC4xID0gIk9sZmFjdG9yeSBlbnNoZWF0aW5nIGNlbGxzIikKCm9lYy5tYXJrZXJzJGRpZmYgPC0gTlVMTApkaWZmIDwtIG9lYy5tYXJrZXJzJHBjdC4xIC0gb2VjLm1hcmtlcnMkcGN0LjIKI0ZDIDwtIGFicyhuZXVyb25zLm1hcmtlcnMkYXZnX2xvZzJGQykKb2VjLm1hcmtlcnMgPC0gY2JpbmQob2VjLm1hcmtlcnMsIGRpZmYpCgpvZWMubWFya2VycyAlPiUgdG9wX24oNTAsIGRpZmYpIC0+IHRvcDUwLmRpZmYKYGBgCgojIEJhclBsb3QKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CmFuaW1hbHMgPC0gdGFibGUocGxldXJvLmRvdHBsb3RAbWV0YS5kYXRhJGFuaW1hbCkKaGVhZGVyIDwtIGMoImFuaW1hbCIsICJmcmVxIikKYW5pbWFscyA8LSBhcy5kYXRhLmZyYW1lKGFuaW1hbHMpCmNvbG5hbWVzKGFuaW1hbHMpIDwtIGhlYWRlcgoKeCA8LSAgZ2dwbG90KGFuaW1hbHMsIGFlcyh4ID0gYW5pbWFsLCB5ID0gZnJlcSkpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoPTAuNzUsIGZpbGw9InN0ZWVsYmx1ZSIpICsKICAgICAgdGhlbWVfY2xhc3NpYygpICsKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICAgICAgbGFicyh0aXRsZT0iTnVtYmVyIG9mIGNlbGxzIHBlciBhbmltYWwiLCB4PSIiLCB5ID0gIk51bWJlciBvZiBjZWxscyIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1mcmVxKSwgdmp1c3Q9LTAuNSwgc2l6ZT0yKSArCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQoKeApgYGAKYGBge3IgZmlnLndpZHRoPTIsIGZpZy5oZWlnaHQ9M30KZGF0YSA8LSB0YWJsZShwbGV1cm9AbWV0YS5kYXRhJGFuaW1hbCkKZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRhdGEpCgpwZXJjZW50YWdlIDwtIGFzLmludGVnZXIocmVwKDAsIDUpKQphID0gMQpmb3IoaSBpbiAxOmxlbmd0aChkYXRhJFZhcjEpKSB7CiAgeCA8LSAoZGF0YSRGcmVxW2FdIC8gc3VtKGRhdGEkRnJlcSkpICogMTAwCiAgcGVyY2VudGFnZVthXSA8LSB4CiAgYSA8LSBhICsgMQp9CmRhdGEkcGVyY2VudGFnZSA8LSBwZXJjZW50YWdlCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKdG90YWwgPC0gZGF0YSRwZXJjZW50YWdlCmFuaW1hbCA8LSBhcy5jaGFyYWN0ZXIoZGF0YSRWYXIxKQpiYXIgPC0gdGliYmxlKHRvdGFsLCBhbmltYWwpCgpsaWJyYXJ5KFJDb2xvckJyZXdlcikKY291bCA8LSBicmV3ZXIucGFsKDUsICJQYXN0ZWwyIikgCgp4IDwtICBnZ3Bsb3QoYmFyLCBhZXMoeCA9ICIiLCB5ID0gdG90YWwsIGZpbGwgPSBhbmltYWwpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aD0wLjc1KSArCiAgICAgIHRoZW1lX2NsYXNzaWMoKSArCiAgICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKSArCiAgICAgIGxhYnModGl0bGU9IlBlcmNlbnRhZ2Ugb2YgY2VsbHMgcGVyIGFuaW1hbCIsIHg9IiIsIHkgPSAiUGVyY2VudGFnZSBvZiBjZWxscyIpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY291bCkKCngKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KcG5nKGZpbGVuYW1lID0gInBsZXVyb19zdGFja2VkYmFycGxvdF9hbmltYWxfV2xlZ2VuZC5wbmciLCB3aWR0aCA9IDMsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwgcmVzID0gNDAwKQp4CmRldi5vZmYoKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEwfQpwZGYoZmlsZSA9ICJwbGV1cm9fc3RhY2tlZGJhcnBsb3RfYW5pbWFsX1dsZWdlbmQucGRmIiwgd2lkdGggPSAzLCBoZWlnaHQgPSA1KQp4CmRldi5vZmYoKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEwfQpwbGV1cm8uZG90cGxvdCA8LSByZWFkUkRTKGZpbGUgPSAicGxldXJvX2RvdHBsb3QucmRzIikKZGF0YSA8LSB0YWJsZShwbGV1cm8uZG90cGxvdEBtZXRhLmRhdGEkYW5pbWFsLCBJZGVudHMocGxldXJvLmRvdHBsb3QpKQpkYXRhX3BlcmNlbnRhZ2UgPC0gYXBwbHkoZGF0YSwgMiwgZnVuY3Rpb24oeCl7eCoxMDAvc3VtKHgsbmEucm09VCl9KQoKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmNvdWwgPC0gYnJld2VyLnBhbCg1LCAiUGFzdGVsMiIpIAp5bmFtZXMgPC0gY29sbmFtZXMoZGF0YV9wZXJjZW50YWdlKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEwfQpwYXIob21hPWMoMywxLDEsMikgKyAwLjUpCmJhcnBsb3QoZGF0YV9wZXJjZW50YWdlLCAKICAgICAgICBjb2wgPSBjb3VsLCAKICAgICAgICBsYXMgPTIsIAogICAgICAgIGZvbnQuYXhpcyA9IDIsIAogICAgICAgIGNleC5sYWIgPSAwLjEsIAogICAgICAgIGxlZ2VuZC50ZXh0ID0gcm93bmFtZXMoZGF0YV9wZXJjZW50YWdlKSwKICAgICAgICBhcmdzLmxlZ2VuZCA9IGxpc3QoeCA9ICJyaWdodCIsIGluc2V0PWMoLTAuMDEsMCksIHhwZCA9IFRSVUUpKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEwfQpwbmcoZmlsZW5hbWUgPSAicGxldXJvX2JhcnBsb3RfYW5pbWFsX1dsZWdlbmQucG5nIiwgd2lkdGggPSAzMCwgaGVpZ2h0ID0gNywgdW5pdHMgPSAiaW4iLCByZXMgPSA0MDApCnBhcihvbWE9YygxLDEsMSwyKSArIDAuNSkKYmFycGxvdChkYXRhX3BlcmNlbnRhZ2UsIAogICAgICAgIGNvbCA9IGNvdWwsIAogICAgICAgIGxhcyA9IDIsIAogICAgICAgIGZvbnQuYXhpcyA9IDIsIAogICAgICAgIGNleC5sYWIgPSAwLjEsIAogICAgICAgIGxlZ2VuZC50ZXh0ID0gcm93bmFtZXMoZGF0YV9wZXJjZW50YWdlKSwKICAgICAgICBhcmdzLmxlZ2VuZCA9IGxpc3QoeCA9ICJyaWdodCIsIGluc2V0PWMoLTAuMDEsMCksIHhwZCA9IFRSVUUpKQpkZXYub2ZmKCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KcGRmKGZpbGUgPSAicGxldXJvX2JhcnBsb3RfYW5pbWFsX1dsZWdlbmQucGRmIiwgd2lkdGggPSAzMCwgaGVpZ2h0ID0gNykKcGFyKG9tYT1jKDEsMSwxLDIpICsgMC41KQpiYXJwbG90KGRhdGFfcGVyY2VudGFnZSwgCiAgICAgICAgY29sID0gY291bCwgCiAgICAgICAgbGFzID0yLCAKICAgICAgICBmb250LmF4aXMgPSAyLCAKICAgICAgICBjZXgubGFiID0gMC4xLCAKICAgICAgICBsZWdlbmQudGV4dCA9IHJvd25hbWVzKGRhdGFfcGVyY2VudGFnZSksCiAgICAgICAgYXJncy5sZWdlbmQgPSBsaXN0KHggPSAicmlnaHQiLCBpbnNldD1jKC0wLjAxLDApLCB4cGQgPSBUUlVFKSkKZGV2Lm9mZigpCmBgYA==